package com.flyiu.data.consistency.service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.flyiu.data.consistency.bean.PartnerMetaDataProcessor.PartnerMetaData;
import com.flyiu.data.consistency.context.DistributedTransactionContext;
import com.flyiu.data.consistency.exception.PartnerCommitException;
import com.flyiu.data.consistency.model.DistributedTransactionPartner;
import com.flyiu.data.consistency.model.DistributedTransactionState;
import com.flyiu.data.consistency.registry.PartnerMeteDateRegistry;
import com.flyiu.data.consistency.repository.DistributedTransactionPartnerRepository;
import com.flyiu.data.consistency.util.ArgumentJsonUtil;

@Component
public class PartnerService {
	@Autowired
	private DistributedTransactionPartnerRepository partnerRepository;

	
	/**
	 * 开启参与者子事务
	 * 
	 * @param name
	 */
	public void start(String name, Object[] args) {
		
		if(DistributedTransactionContext.getInstance()==null) {
			return;
		}
		
		DistributedTransactionPartner partner = new DistributedTransactionPartner();
		partner.setTransactionState(DistributedTransactionState.INIT);
		partner.setInstance(DistributedTransactionContext.getInstance());
		partner.setName(name);

		partner.setContext(ArgumentJsonUtil.toJson(args));

		DistributedTransactionContext.getInstance().getPartners().add(partner);
		partnerRepository.save(partner);
	}
	
	private Method getMethod(PartnerMetaData metaData, boolean commit){
		Method method = null;
		if (commit) {
			method = metaData.getCommitMethod();
		} else {
			method = metaData.getRollbackMethod();
		}
		return method;
	}

	private void invoke(DistributedTransactionPartner partner, boolean commit) {
		PartnerMetaData metaData = PartnerMeteDateRegistry.getPartnerMeteData(partner.getName());
		try {
			Method method = getMethod(metaData, commit);
			if(method!=null) {
			String context = partner.getContext();
			Object[] param = ArgumentJsonUtil.toArg(context, metaData.getParameterTypes());

			method.invoke(metaData.getBean(), param);
			}else {
				
			}
			
			partner.setTransactionState(commit?DistributedTransactionState.COMMIT:DistributedTransactionState.CANCEL);
			
			partnerRepository.save(partner);
		} catch (Exception e) {
			throw new PartnerCommitException(partner.getId()+"", e.getCause());
		}
	}

	public void commit(DistributedTransactionPartner distributedTransactionPartner) {
		invoke(distributedTransactionPartner, true);

	}

	public void rollback(DistributedTransactionPartner distributedTransactionPartner) {
		invoke(distributedTransactionPartner, false);
	}
}
